package com.jesse.custom.asyncimageview;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.jesse.dao.UpdateEvent;
import com.jesse.util.AppUtil;
import com.jesse.util.MyUtil;
import com.jesse.util.ViewUtil;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;

/**
 * 图片下载核心类，包含图片LruCache内存缓存，与SD卡本地缓存，只需要指定下载地址，与图片适配宽度即可。
 * 
 * 未指定宽高情况下，请默认传0
 * 
 * 所有下载状态回调均使用{@link AsynImageDownloadDao}接口
 * 
 * @author jesse
 * 
 */
public class AsyncImageLoader {

	public static final int LOADING = -1;
	public static final int COMPLETE = 100;
	/**
	 * AsyncImageLoader 单例
	 */
	private static AsyncImageLoader instance = null;
	
	/**
	 * ImageView内存缓存
	 */
	private LruCache<String, Bitmap> memoryCache;
	
	/**
	 * 下载路径
	 */
	private AsyncImagePathDefine patchDefine;
	
	/**
	 * 本地文件读写类
	 */
	private AsyncImageFileUtil fileUtil;
	
	/**
	 * 文件下载，记录同步缓存，当同一张图多次被下载时，只下载第一次请求，其他相同请求存入map同步
	 */
	private Map<String, List<AsynImageDownloadDao>> downloadMap = new HashMap<String, List<AsynImageDownloadDao>>();
	
	/**
	 * Image下载线程池
	 */
	private ExecutorService mImageThreadPool = null;
	
	public static AsyncImageLoader getInstance() {
		if (instance == null) {
			synchronized (AsyncImageLoader.class) {
				if (instance == null) {
					instance = new AsyncImageLoader();
				}
			}
		}
		return instance;
	}
	
	private AsyncImageLoader() {
		getThreadPool();
		initLruCache();
		initPatch();
	}
	
	private ExecutorService getThreadPool() {
		if (mImageThreadPool == null) {
			mImageThreadPool = Executors.newFixedThreadPool(2);
		}
		return mImageThreadPool;
	}
	
	private void initLruCache() {
		int maxMemory = (int) Runtime.getRuntime().maxMemory() ;
		ViewUtil.Debug("maxMemory is  " + maxMemory / 1024 / 1024 + "MB");
		int cacheSize = maxMemory / 8;
		ViewUtil.Debug("cacheSize is   " + cacheSize / 1024 / 1024 + "MB");
		memoryCache = new LruCache<String, Bitmap>(cacheSize) {
			
			@Override
			protected int sizeOf(String key, Bitmap bitmap) {
				return bitmap.getByteCount();
			}
			
			@Override
			protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue){
				ViewUtil.Debug(key + "is gone ");
			}
		};
	}
	
	private void initPatch() {
		AsyncImagePathDefine patchDefine = new AsyncImagePathDefine() {
			
			@Override
			public void setIconFilePath() {
				super.iconFilePath = "/Android/data/" + ViewUtil.getContext().getPackageName() + "/cache/";
			}
			
			@Override
			public void setDownloadPath() {
				super.downloadPath = "/Photo/" + AppUtil.getApplicationName(ViewUtil.getContext(), ViewUtil.getContext().getPackageName()) +"/";
			}
		};
		setPatchDefine(patchDefine);
	}
	
	/**
	 * 初始化定义下载路径
	 * @param define
	 */
	public void setPatchDefine(AsyncImagePathDefine define) {
		this.patchDefine = define;
		fileUtil = new AsyncImageFileUtil(patchDefine);
	}
	
	public AsyncImagePathDefine getPatchDefine() {
		return patchDefine;
	}
	
	public AsyncImageFileUtil getFileUtil() {
		return fileUtil;
	}
	
	/**
	 * 从LruCache中获取一张图片，如果不存在就返回null。
	 * 
	 * @param key
	 *            LruCache的键,默认为url+宽度+高度的Md5值
	 * @return 对应传入键的Bitmap对象，或者null。
	 */
	public Bitmap getBitmapFromMemoryCache(String key) {
		return memoryCache.get(key);
	}
	
	/**
	 * 将一张图片存储到LruCache中。
	 * 
	 * @param key
	 *            LruCache的键，这里传入图片的URL地址。
	 * @param bitmap
	 *            LruCache的键，这里传入从网络上下载的Bitmap对象。
	 */
	public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
		if (getBitmapFromMemoryCache(key) == null && bitmap != null) {
			memoryCache.put(key, bitmap);
		}
	}
	
	/**
	 * 清除全部内存缓存
	 */
	public void clearMemoryCache() {
        if (memoryCache != null) {
            if (memoryCache.size() > 0) {
                ViewUtil.Debug("mMemoryCache.size() " + memoryCache.size());
                memoryCache.evictAll();
                ViewUtil.Debug("mMemoryCache.size()" + memoryCache.size());
            }
        }
    }
	
	/**
	 * 清除单图内存缓存
	 * @param key	图片key
	 */
	public void removeBitmapFromMemoryCache(String key) {
		if (key != null) {
            if (memoryCache != null) {
                Bitmap bm = memoryCache.remove(key);
                if (bm != null) {
                	bm.recycle();
                }
            }
        }
	}

	public void saveBitmapInSDCard(Bitmap bitmap, String imagePath) {
		fileUtil.writeDrawableInSD(bitmap, imagePath);
	}
	
	public void saveBitmapInSDCache(String key, Bitmap bitmap) {
		fileUtil.writeDrawableCacheByName(bitmap, key);
	}
	
	public void saveBitmapInSDDownload(String key, Bitmap bitmap) {
		fileUtil.writeDrawableDownloadByName(bitmap, key);
	}

	public void downloadImageSD(String imageUrl, String iconKey, int scaleWidth, int scaleHight, AsynImageDownloadDao imageCallback) {
		downloadImage(imageUrl, iconKey, scaleWidth, scaleHight, fileUtil.getImageDownloadPath(iconKey), imageCallback);
	}
	
	/**
	 * 读取图片核心方法
	 * 
	 * @param imageUrl
	 * 			图片下载地址
	 * @param iconKey
	 * 			图片标示符
	 * @param scaleWidth
	 * 			需要缩放的宽度
	 * @param scaleHight
	 * 			需要缩放的高度
	 * @param imageCallback {@link AsynImageDownloadDao}
	 * 			回调
	 * @return
	 */
	@SuppressLint("HandlerLeak")
	public synchronized Bitmap loadDrawable(final String imageUrl, final String iconKey, final int scaleWidth, final int scaleHight,
			final AsynImageDownloadDao imageCallback) {
		
		/**
		 * 先返回null代表loading
		 */
		imageCallback.imageLoaded(null, imageUrl, LOADING);
		
		Bitmap imageBitmap = getBitmapFromMemoryCache(iconKey);
		//从内存中读取
		if (imageBitmap != null) {
			imageCallback.imageLoaded(imageBitmap, imageUrl, COMPLETE);
			ViewUtil.Debug(imageUrl + "读取内存");
			return imageBitmap;
		}
		
		//从本地读取
		imageBitmap = loadFromSDCard(iconKey, scaleWidth, scaleHight);
		if (imageBitmap != null) {
			imageCallback.imageLoaded(imageBitmap, imageUrl, COMPLETE);
			ViewUtil.Debug(imageUrl + "读取本地");
			return imageBitmap;
		}
		
		//从网络下载
		downloadImage(imageUrl, iconKey, scaleWidth, scaleHight, fileUtil.getImageCachePath(iconKey, imageUrl), new AsynImageDownloadDao() {
			
			@Override
			public void imageLoaded(Bitmap imageBitmap, String imageUrl, Object msg) {
				imageCallback.imageLoaded(imageBitmap, imageUrl, (Integer) msg);
			}
		});
		return null;
	}
	
	/**
	 * 下载图片方法
	 * @param imageUrl
	 * @param iconKey
	 * @param scaleWidth
	 * @param scaleHight
	 * @param path
	 * @param imageCallback
	 */
	private void downloadImage(final String imageUrl, final String iconKey, final int scaleWidth, final int scaleHight, final String path,
			final AsynImageDownloadDao imageCallback) {
		if (checkAndAddInMap(iconKey, imageCallback)) {
			return;
		}
		final Handler handler = new Handler() {
			public void handleMessage(Message message) {
				switch (message.what) {
				case 1:
					callBackDownloadInMap(iconKey, (Bitmap) message.obj, imageUrl, COMPLETE);
					deleteCallBackInMap(iconKey);
					break;
				case 2:
					callBackDownloadInMap(iconKey, null, imageUrl, (Integer) message.obj);
					break;
				}
			}
		};
		
		getThreadPool().execute(new Runnable() {
			
			@Override
			public void run() {
				checkFrilUtil();
				Bitmap bitmap;
				try {
					bitmap = loadImageFromUrl(imageUrl, iconKey, scaleWidth, scaleHight, path, new UpdateEvent() {
						
						@Override
						public void update(Object event) {
							Message.obtain(handler, 2, event).sendToTarget();
						}
					});
					if (bitmap != null) {
						Message.obtain(handler, 1, bitmap).sendToTarget();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	/**
	 * 检查图片是否有进入下载，有进入则添加回掉接口至Map中
	 * @param key	图片key
	 * @param downloadDao	回掉
	 * @return
	 */
	private boolean checkAndAddInMap(String key, AsynImageDownloadDao downloadDao) {
		if (downloadMap.get(key) == null) {
			List<AsynImageDownloadDao> item = new ArrayList<AsynImageDownloadDao>();
			item.add(downloadDao);
			downloadMap.put(key, item);
			return false;
		}
		List<AsynImageDownloadDao> item = downloadMap.get(key);
		item.add(downloadDao);
		return true;
	}
	
	/**
	 * 调取下载图片回掉接口
	 * @param key	图片key	
	 * @param imageBitmap	下载下来的图片
	 * @param imageUrl		图片地址	
	 * @param msg			附加信息
	 */
	private void callBackDownloadInMap(String key, Bitmap imageBitmap, String imageUrl, Object msg) {
		if (downloadMap.get(key) == null) {
			throw new NullPointerException("There is a error in AsyncImageLoader. The downloadMap dont have the right key.Please feedback to us");
		}
		List<AsynImageDownloadDao> item = downloadMap.get(key);
		for (AsynImageDownloadDao dao : item) {
			dao.imageLoaded(imageBitmap, imageUrl, msg);
		}
	}
	
	/**
	 * 图片下载完毕，删除回掉接口
	 * @param key	图片key
	 */
	private void deleteCallBackInMap(String key) {
		Iterator iterator = downloadMap.keySet().iterator();
		while (iterator.hasNext()) {
			if (key.equals((String)iterator.next())){
				iterator.remove();
			}
		}
	}
	
	/**
	 * 取消正在下载的任务
	 */
	public synchronized void cancelTask() {
		if(mImageThreadPool != null){
			mImageThreadPool.shutdownNow();
			mImageThreadPool = null;
		}
	}
	
	private Bitmap loadImageFromUrl(String imageUrl, String iconKey, int scaleWidth, int scaleHight, String path, UpdateEvent updateDao) throws IOException {
		ViewUtil.Debug(imageUrl + "下载");
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
		} 
		HttpURLConnection con = null;
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		BufferedInputStream bis = null;
		File imageFile = null;
		int fileSize = 0;
		try {
			URL url = new URL(imageUrl);
			con = (HttpURLConnection) url.openConnection();
			con.setConnectTimeout(5 * 1000);
			con.setReadTimeout(15 * 1000);
			con.setDoInput(true);
			fileSize = con.getContentLength();
			bis = new BufferedInputStream(con.getInputStream());
			imageFile = new File(path);
			fos = new FileOutputStream(imageFile);
			bos = new BufferedOutputStream(fos);
			byte[] b = new byte[1024];
			int length;
			int curSize = 0;
			int times = 0;
			int progress = 0;
			while ((length = bis.read(b)) != -1) {
				bos.write(b, 0, length);
				bos.flush();
				curSize += length;
				if (updateDao != null) {
					updateDao.update(progress);
				}
				progress = (int) ((curSize / (float) fileSize) * 100); 
				times++;
				if(curSize == 99){
					if (updateDao != null) {
						updateDao.update(COMPLETE);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (bis != null) {
					bis.close();
				}
				if (bos != null) {
					bos.close();
				}
				if (con != null) {
					con.disconnect();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		Bitmap bitmap = null;
		if (imageFile != null) {
			bitmap = decodeSampledBitmapFromResource(path, scaleWidth, scaleHight);
		}
		return bitmap;
	}
	
	private Bitmap loadFromSDCard(String iconKey, int scaleWidth, int scaleHight) {
		checkFrilUtil();
		Bitmap bitmap = fileUtil.obtainBitmapBykey(iconKey, scaleWidth, scaleHight);
		if (bitmap != null) {
			addBitmapToMemoryCache(iconKey, bitmap);
		}
		return bitmap;
	}
	
	public static Bitmap decodeSampledBitmapFromResource(String pathName, int scaleWdith, int scaleHeight) {
		// 第一次解析将inJustDecodeBounds设置为true，来获取图片大小
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(pathName, options);
		int imageHeight = options.outHeight;  
		int imageWidth = options.outWidth;
		options.inSampleSize = calculateInSampleSize(imageWidth, imageHeight, scaleWdith, scaleHeight);
		options.inJustDecodeBounds = false;
		options.inPreferredConfig = Config.RGB_565;
		options.inPurgeable = true;// 允许可清除
		options.inInputShareable = true;// 以上options的两个属性必须联合使用才会有效果
		return BitmapFactory.decodeFile(pathName, options);
	}
	
	public static int calculateInSampleSize(int originalWidth, int originalHeight, int scaleWidth, int scaleHight) {
		int inSampleSize = 1;  
	    if (scaleWidth != 0 && scaleHight == 0 && originalWidth > scaleWidth) {  
	        inSampleSize  = Math.round((float) originalWidth / (float) scaleWidth);  
	    }  
	    
	    if (scaleHight != 0 && scaleWidth == 0 && originalHeight > scaleHight) {  
	    	inSampleSize = Math.round((float) originalHeight / (float) scaleHight);  
	    }  
	    
	    if (scaleHight != 0 && scaleWidth != 0 && (originalHeight > scaleHight || originalWidth > scaleWidth)) { 
	    	final int widthRatio = Math.round((float) originalWidth / (float) scaleWidth);  
	    	final int heightRatio = Math.round((float) originalHeight / (float) scaleHight);  
	        // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高  
	        // 一定都会大于等于目标的宽和高。  
	        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;  
	    }
	    return inSampleSize;
	}
	
	public void downloadBitmap() {
		
	}
	
	private void checkFrilUtil() {
		if (patchDefine == null) {
			throw new NullPointerException("Please use the function setPatchDefine before first times use function IconView");
		}
	}
}
